package com.me.myFramework.aop22.aop121;

import com.me.myFramework.aop22.aop121.intercept.method.AfterReturningAdviceInterceptor121;
import com.me.myFramework.aop22.aop121.intercept.method.MethodBeforeAdviceInterceptor121;
import com.me.myFramework.aop22.aop121.intercept.method.MethodInterceptor121;
import com.me.myFramework.aop22.aop121.intercept.method.ReflectiveMethodInvocation121;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

/**
 * 《AOP-121》：  <br><br>
 * TODO getProxy 也使用链式调用逻辑，根据当前 MethodInterceptor 在链条中的 index 判断：<br>
 *          -- 如果当前节点是责任链的最后一个节点，直接调用目标方法；<br>
 *          -- 如果当前节点不是责任链的最后一个节点，需要进行链式调用。
 */
public class ProxyFactory121 {

    private final Object target;
    private final List<Advice> advices = new ArrayList<>();

    public ProxyFactory121(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> {
            List<MethodInterceptor121> interceptors = getInterceptors();
            if (interceptors.size() > 0) {
                // retVal = interceptor.invoke(method, args, target);
                Class<?> targetClass = target.getClass();
                // TODO 把 原调用中散装的参数统一封装到 ReflectiveMethodInvocation 中
                MethodInvocation mi = new ReflectiveMethodInvocation121(proxy, target, method, args, targetClass, interceptors);

                // return interceptors.get(0).invoke(mi);
                // TODO 也修改为链式调用逻辑
                ReflectiveMethodInvocation121 invocation = (ReflectiveMethodInvocation121) mi;
                int index = invocation.getCurrentIndex();
                if (index == invocation.getInterceptorSize() - 1) {
                    return mi.getMethod().invoke(mi.getThis(), mi.getArguments());
                }
                else {
                    return invocation.getNextInterceptor().invoke(mi);
                }
            }
            else {
                return method.invoke(target, args);
            }

        });
    }

    private List<MethodInterceptor121> getInterceptors() {
        List<MethodInterceptor121> interceptorList = new ArrayList<>(this.advices.size());
        for (Advice advice : this.advices) {
            if (advice instanceof MethodBeforeAdvice) {
                interceptorList.add(new MethodBeforeAdviceInterceptor121((MethodBeforeAdvice) advice));
            } else if (advice instanceof AfterReturningAdvice) {
                interceptorList.add(new AfterReturningAdviceInterceptor121((AfterReturningAdvice) advice));
            } else {
                // 实际是不存在这种情况的，因为 Advice 的子接口是自己定义的，也都会在这边判断
                throw new RuntimeException("GG, 未知的 Advice");
            }
        }

        return interceptorList;
    }

    public void addAdvice(Advice advice) {
        this.advices.add(advice);
    }

    private boolean isCustomMethod(String methodName) {
        return !("toString".equals(methodName) || "hashCode".equals(methodName) || "equals".equals(methodName)
                || "clone".equals(methodName));
    }

}
